export class WebGPUMipmapGenerator {
  constructor(device) {
    this.device = device;
    this.sampler = device.createSampler({ minFilter: 'linear' });
    // 我们需要为使用的每种纹理格式创建一个新的管道。
    this.pipelines = {};
  }

  async getMipmapPipeline (format) {
    let pipeline = this.pipelines[format];
    if (!pipeline) {
      // 着色器模块在所有管道之间共享，所以只创建一次。
      if (!this.mipmapShaderModule) {
        this.mipmapShaderModule = this.device.createShaderModule({
          label: 'Mipmap Generator',
          code: `
            var<private> pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
              vec2<f32>(-1.0, -1.0), vec2<f32>(-1.0, 3.0), vec2<f32>(3.0, -1.0));

            struct VertexOutput {
              @builtin(position) position : vec4<f32>,
              @location(0) texCoord : vec2<f32>,
            };

            @vertex
            fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput {
              var output : VertexOutput;
              output.texCoord = pos[vertexIndex] * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
              output.position = vec4<f32>(pos[vertexIndex], 0.0, 1.0);
              return output;
            }

            @group(0) @binding(0) var imgSampler : sampler;
            @group(0) @binding(1) var img : texture_2d<f32>;

            @fragment
            fn fragmentMain(@location(0) texCoord : vec2<f32>) -> @location(0) vec4<f32> {
              return textureSample(img, imgSampler, texCoord);
            }
          `,
        });

        this.bindGroupLayout = this.device.createBindGroupLayout({
          label: 'Mipmap Generator',
          entries: [{
            binding: 0,
            visibility: GPUShaderStage.FRAGMENT,
            sampler: {},
          }, {
            binding: 1,
            visibility: GPUShaderStage.FRAGMENT,
            texture: {
              sampleType: "float",
              viewDimension: "2d",
            },
          }]
        });
        this.pipelineLayout = this.device.createPipelineLayout({
          label: 'Mipmap Generator',
          bindGroupLayouts: [this.bindGroupLayout],
        })
      }

      pipeline = await this.device.createRenderPipelineAsync({
        layout: this.pipelineLayout,
        vertex: {
          module: this.mipmapShaderModule,
          entryPoint: 'vertexMain',
        },
        fragment: {
          module: this.mipmapShaderModule,
          entryPoint: 'fragmentMain',
          targets: [{ format }],
        }
      });
      this.pipelines[format] = pipeline;
    }
    return pipeline;
  }

  /**
   *从级别0的数据为给定的GPUTexture生成mipmaps。
   *
   * @param {module:External.GPUTexture} texture - 生成贴图的纹理.
   * @param {object} textureDescriptor -创建纹理的GPUTextureDescriptor.
   * @returns {module:External.GPUTexture} - 最初传递的纹理
   */
  async generateMipmap (texture, textureDescriptor) {
    // TODO: 是否需要以不同的方式处理sRGB格式??
    const mipmapShaderModule = this.device.createShaderModule({
      code: `
var<private> pos : array<vec2<f32>, 4> = array<vec2<f32>, 4>(
vec2<f32>(-1.0, 1.0), vec2<f32>(1.0, 1.0),
vec2<f32>(-1.0, -1.0), vec2<f32>(1.0, -1.0));

struct VertexOutput {
@builtin(position) position : vec4<f32>,
@location(0) texCoord : vec2<f32>
};

@vertex
fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput {
var output : VertexOutput;
output.texCoord = pos[vertexIndex] * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
output.position = vec4<f32>(pos[vertexIndex], 0.0, 1.0);
return output;
}

@binding(0) @group(0) var imgSampler : sampler;
@binding(1) @group(0) var img : texture_2d<f32>;

@fragment
fn fragmentMain(@location(0) texCoord : vec2<f32>) -> @location(0) vec4<f32> {
var outColor: vec4<f32> = textureSampleLevel(img, imgSampler, texCoord , 0.0 );
return outColor;
}
`,
    });
    const pipeline = await this.device.createRenderPipelineAsync({
      layout: `auto`,
      vertex: {
        module: mipmapShaderModule,
        entryPoint: 'vertexMain',
      },
      fragment: {
        module: mipmapShaderModule,
        entryPoint: 'fragmentMain',
        targets: [
          {
            format: textureDescriptor.format, // Make sure to use the same format as the texture
          },
        ],
      },
      primitive: {
        topology: 'triangle-strip',
        stripIndexFormat: 'uint32',
      },
    });

    if (textureDescriptor.dimension == '3d' || textureDescriptor.dimension == '1d') {
      throw new Error('目前不支持为非2d纹理生成mipmap!');
    }
    let sampler;
    if (texture.format == `rgba16float`) {
      sampler = this.device.createSampler({
        minFilter: `nearest`,
        magFilter: `linear`,
      });
    } else {
      sampler = this.device.createSampler({
        minFilter: `linear`,
        magFilter: `linear`,
      });
    }

    let srcView = texture.createView({
      baseMipLevel: 0,
      mipLevelCount: 1,
    });
    const commandEncoder = this.device.createCommandEncoder();
    for (let i = 1; i < textureDescriptor.mipLevelCount; ++i) {
      const dstView = texture.createView({
        baseMipLevel: i, // 确保我们得到正确的mip水平...
        mipLevelCount:1, // 并且只选择一个mip级别
      });

      const passEncoder = commandEncoder.beginRenderPass({
        colorAttachments: [
          {
            view: dstView, // 渲染通道使用下一个mip级别作为渲染附件.
            clearValue: [0, 0, 0, 0],
            loadOp: `clear`,
            storeOp: 'store',
          },
        ],
      });


      const bindGroup = this.device.createBindGroup({
        layout: pipeline.getBindGroupLayout(0),
        entries: [
          {
            binding: 0,
            resource: sampler,
          },
          {
            binding: 1,
            resource: srcView,
          },
        ],
      });


      passEncoder.setPipeline(pipeline);
      passEncoder.setBindGroup(0, bindGroup);
      passEncoder.draw(4);
      passEncoder.end();

      srcView = dstView;
    }
    this.device.queue.submit([commandEncoder.finish()]);

    // let mipTexture = texture;
    // const arrayLayerCount = textureDescriptor.size.depthOrArrayLayers || 1; // 只对2D纹理有效。

    // // 如果纹理是使用RENDER_ATTACHMENT创建的，我们可以在mip级别之间直接渲染.
    // const renderToSource = textureDescriptor.usage & GPUTextureUsage.RENDER_ATTACHMENT;
    // if (!renderToSource) {
    //   // 否则我们必须使用一个单独的纹理来渲染。它可以比源纹理小一个mip级别，因为我们已经有了顶层。
    //   const mipTextureDescriptor = {
    //     size: {
    //       width: Math.max(1, textureDescriptor.size.width >>> 1),
    //       height: Math.max(1, textureDescriptor.size.height >>> 1),
    //       depthOrArrayLayers: arrayLayerCount,
    //     },
    //     format: textureDescriptor.format,
    //     usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
    //     mipLevelCount: textureDescriptor.mipLevelCount - 1,
    //   };
    //   mipTexture = this.device.createTexture(mipTextureDescriptor);
    // }

    // const commandEncoder = this.device.createCommandEncoder({});
    // for (let arrayLayer = 0; arrayLayer < arrayLayerCount; ++arrayLayer) {
    //   let srcView = texture.createView({
    //     baseMipLevel: 0,
    //     mipLevelCount: 1,
    //     dimension: '2d',
    //     baseArrayLayer: arrayLayer,
    //     arrayLayerCount: 1,
    //   });

    //   let dstMipLevel = renderToSource ? 1 : 0;
    //   for (let i = 1; i < textureDescriptor.mipLevelCount; ++i) {
    //     const dstView = mipTexture.createView({
    //       baseMipLevel: dstMipLevel++,
    //       mipLevelCount: 1,
    //       dimension: '2d',
    //       baseArrayLayer: arrayLayer,
    //       arrayLayerCount: 1,
    //     });

    //     const passEncoder = commandEncoder.beginRenderPass({
    //       colorAttachments: [{
    //         view: dstView,
    //         loadOp: 'clear',
    //         storeOp: 'store'
    //       }],
    //     });

    //     const bindGroup = this.device.createBindGroup({
    //       layout: this.bindGroupLayout,
    //       entries: [{
    //         binding: 0,
    //         resource: this.sampler,
    //       }, {
    //         binding: 1,
    //         resource: srcView,
    //       }],
    //     });

    //     passEncoder.setPipeline(pipeline);
    //     passEncoder.setBindGroup(0, bindGroup);
    //     passEncoder.draw(3, 1, 0, 0);
    //     passEncoder.end();

    //     srcView = dstView;
    //   }
    // }

    // // 如果我们没有渲染到源纹理，通过复制临时mipmap纹理的mip结果到源纹理来完成。
    // if (!renderToSource) {
    //   const mipLevelSize = {
    //     width: Math.max(1, textureDescriptor.size.width >>> 1),
    //     height: Math.max(1, textureDescriptor.size.height >>> 1),
    //     depthOrArrayLayers: arrayLayerCount,
    //   };

    //   for (let i = 1; i < textureDescriptor.mipLevelCount; ++i) {
    //     commandEncoder.copyTextureToTexture({
    //       texture: mipTexture,
    //       mipLevel: i-1,
    //     }, {
    //       texture: texture,
    //       mipLevel: i,
    //     }, mipLevelSize);

    //     mipLevelSize.width = Math.max(1, mipLevelSize.width >>> 1);
    //     mipLevelSize.height = Math.max(1, mipLevelSize.height >>> 1);
    //   }
    // }

    // this.device.queue.submit([commandEncoder.finish()]);

    // if (!renderToSource) {
    //   mipTexture.destroy();
    // }

    return texture;
  }
}
